﻿
using System.Diagnostics.Metrics;
using System.Linq;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Cecil.Rocks;
using Mono.Cecil.Pdb;

public class LuaModuleDefinition
{
    private readonly IEnumerable<LuaTypeDefinition> _types;
    private readonly AssemblyDefinition _assembly;
    private readonly ModuleDefinition _self;
    private readonly IDictionary<string, LuaTypeReference> _typeMap;
    private readonly ISymbolReader _symbol;
    private string _output;
    public LuaModuleDefinition(string input, string output)
    {
        _typeMap = new Dictionary<string, LuaTypeReference>();

        _assembly = AssemblyDefinition.ReadAssembly(input);
        var pdbPath = Path.ChangeExtension(input, "pdb");
        if (File.Exists(pdbPath))
        {
            var pdbProvider = new PdbReaderProvider();
            _symbol = pdbProvider.GetSymbolReader(_assembly.MainModule, pdbPath);
            _assembly.MainModule.ReadSymbols(_symbol);
        }
        _self = _assembly.MainModule;
        _types = from type in _self.GetAllTypes()
                 where !type.IsBaseOf<Attribute>()
                 where type.Attributes != Mono.Cecil.TypeAttributes.Interface
                 select new LuaTypeDefinition(this, type);
    }
    public string GetDefaultValue(MemberReference type)
    {
        switch (type.FullName)
        {
            case "System.Value":
                break;
        }
        return "nil";
    }
    public bool TryGetTypeDefinition(string fullName, out LuaTypeDefinition reference)
    {
        if (!_typeMap.TryGetValue(fullName, out var reference2))
        {
            var definition = _self.GetType(fullName);
            if (definition != null)
            {
                _typeMap[fullName] = reference = new LuaTypeDefinition(this, definition);
                return true;
            }
        }
        reference = reference2 as LuaTypeDefinition;
        return reference != null;
    }
    public bool TryGetTypeReference(string fullName, out LuaTypeReference reference)
    {
        if (_typeMap.TryGetValue(fullName, out reference))
            return true;
        var definition = _self.GetType(fullName);
        if (definition != null)
        {
            _typeMap[fullName] = reference = new LuaTypeDefinition(this, definition);
        }
        else if (_self.TryGetTypeReference(fullName, out var reference2))
        {
            _typeMap[fullName] = reference = new LuaTypeReference(this, reference2);
        }
        return reference != null;
    }
    public LuaTypeReference GetTypeReference(TypeReference type)
    {
        if (!TryGetTypeReference(type.FullName, out var reference))
        {
            _typeMap[type.FullName] = reference = new LuaTypeReference(this, type);
        }
        return reference;
    }
    public LuaTypeReference GetTypeReference(string fullName)
    {
        if (!TryGetTypeReference(fullName, out var reference))
        {
            throw new ArgumentException("没有改类型！" + fullName);
        }
        return reference;
    }
    internal void WriteTo(ICodeWriter writer)
    {
        writer.PushColor(ConsoleColor.Green);
        writer.Comment($"module:{_self.FileName}");
        writer.Indent++;
        foreach (var type in _types)
            type.WriteTo(writer);
        writer.Indent--;
        writer.PopColor();
    }

    internal string TryVariableName(MethodDefinition method, VariableDefinition variable)
    {
        if (_symbol == null)
            return variable.ToString();
        if (_symbol.Read(method).TryGetName(variable, out var name))
        {
            return name;
        }
        return variable.ToString();
    }
}

